package net.gdface.facedb;

import net.gdface.facedb.db.FeatureBean;
import net.gdface.facedb.db.ImageBean;
import net.gdface.sdk.BaseFaceApi;
import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.CompareResult;
import net.gdface.sdk.FaceApi;
import net.gdface.sdk.NotFaceDetectedException;
import net.gdface.sdk.fse.FeatureSe;

import static com.gitee.l0km.com4j.base.BinaryUtils.*;
import static com.gitee.l0km.com4j.base.SimpleLog.log;
import static com.google.common.base.Preconditions.*;

import java.util.Arrays;
import java.util.List;

import com.gitee.l0km.com4j.base.exception.NotFoundBeanException;
import com.gitee.l0km.ximage.ImageErrorException;
import com.gitee.l0km.ximage.MatType;
import com.google.common.base.MoreObjects;

/**
 * 实现{@link FaceDb}人脸识别算法相关方法
 * @author guyadong
 *
 */
public class FacedbFull extends  FacedbLocal {
	private final FaceApi faceApi;
	private FeatureDbSearchEngine fdbse;
	private boolean multiFaceFeature;
	public FacedbFull(FaceApi faceApi) {
		super();
		this.faceApi = checkNotNull(faceApi,"faceApi is null");
		this.multiFaceFeature = Boolean.valueOf(MoreObjects.firstNonNull(this.faceApi.sdkCapacity().get("MULTI_FACE_FEATURE"),"false"));
		FeatureSe fse = BaseFaceApi.getFeatureSeInstance(faceApi);
		if(null != fse){
			fdbse= new FeatureDbSearchEngine(fse);
			fdbse.init();
			capacity.put(C_SUPPORT_SEARCH, Boolean.TRUE.toString());
		}else{
			log("WARN:DISABLE feature search engine");
		}
		// 装入 FaceApi能力描述
		capacity.putAll(faceApi.sdkCapacity());
	}

	public FaceApi getFaceApi() {
		return faceApi;
	}

	@Override
	protected void checkMulti() {
		if(!multiFaceFeature){
			throw new UnsupportedOperationException();
		}
	}

	@Override
	protected void checkNotMulti() {
		if(multiFaceFeature){
			throw new UnsupportedOperationException();
		}
	}

	@Override
	public CodeInfo[] detectAndGetCodeInfo(byte[] imgData) throws ImageErrorException {
		try {
			return getFaceApi().detectAndGetCodeInfo(imgData,0);
		} catch (NotFaceDetectedException e) {
			return new CodeInfo[0];
		}
	}
	@Override
	public CodeInfo[] detectAndGetCodeInfo(MatType matType,byte[] matData, int width, int height) throws ImageErrorException {
		try {
			return getFaceApi().matDetectAndGetCodeInfo(matType,matData,width,height,0);
		} catch (NotFaceDetectedException e) {
			return new CodeInfo[0];
		}
	}
	@Override
	public ImageBean addImageIfAbsent(byte[] imgData, CodeInfo code, double similarty)
			throws NotFaceDetectedException, ImageErrorException {
		checkNotMulti();
		if(null == code){
			code = getFaceApi().detectCenterFace(imgData);
		}
		if(null == code.getCode()){
			code = getFaceApi().getCodeInfo(imgData, 1, new CodeInfo[]{code})[0];
		}
		SearchResult[] result = searchFeatures(code.getCode(), similarty, 1, null);
		if (result.length > 0) {
			// 返回相似度最高的记录
			return getImageByFeatureId(toHex(result[0].getFeatureId()));
		}else{
			try {
				// 没有相似记录时添加到数据库
				return addImage( imgData, Arrays.asList(code));
			} catch (DuplicateRecordException e) {
				return getImage(getMD5String(imgData));
			}
		}
	}
	@Override
	public double compareFeature(String featureId, byte[] feature) throws NotFoundBeanException {
		byte[] f1 = getBytesInBuffer(getFeatureChecked(featureId).getFeature());
		return getFaceApi().compareCode(f1,feature);
	}

	@Override
	public double compareFeatureId(String featureId1, String featureId2) throws NotFoundBeanException {
		byte[] f1 = getBytesInBuffer(getFeatureChecked(featureId1).getFeature());
		byte[] f2 = getBytesInBuffer(getFeatureChecked(featureId2).getFeature());
		return getFaceApi().compareCode(f1,f2);
	}

	@Override
	public double[] compareFeatures(String featureId, CodeInfo[] features) throws NotFoundBeanException {
		byte[] f1 = getBytesInBuffer(getFeatureChecked(featureId).getFeature());
		return getFaceApi().compareCodes(f1, features);
	}

	@Override
	public double[] compareFaces(String featureId, byte[] imgData, CodeInfo[] facePos)
			throws NotFoundBeanException, NotFaceDetectedException {
		checkArgument(null !=facePos,"facePos is null");
		if(facePos.length == 0){
			return new double[0];
		}
		facePos = getFaceApi().getCodeInfo(imgData, facePos.length, facePos);
		return compareFeatures(featureId,facePos);
	}

	@Override
	public CompareResult detectAndCompareFaces(String featureId, byte[] imgData, int faceNum)
			throws NotFoundBeanException, ImageErrorException, NotFaceDetectedException {
		FeatureBean featureBean = getFeatureChecked(featureId);
		return getFaceApi().compareFaces(getBytesInBuffer(featureBean.getFeature()), imgData, faceNum);
	}

	@Override
	public SearchResult[] detectAndSearchFaces(byte[] imgData, double similarty, int rows, String where) throws ImageErrorException, NotFaceDetectedException {
		CodeInfo code = getFaceApi().detectAndGetCodeInfo(imgData, 1)[0];
		return searchFeatures(code.getCode(), similarty, rows, where);
	}

	@Override
	public ImageBean detectAndAddFeatures(byte[] imgData, int faceNum)
			throws DuplicateRecordException, ImageErrorException, NotFaceDetectedException {
		checkNotMulti();
		List<CodeInfo> codes = Arrays.asList(getFaceApi().detectAndGetCodeInfo(imgData, faceNum));
		return addImage(imgData,codes);
	}

	@Override
	public SearchResult[] searchFaces(byte[] imgData, CodeInfo facePos, double similarty, int rows, String where)
			throws NotFaceDetectedException, ImageErrorException {
		if(facePos == null){
			facePos = getFaceApi().detectAndGetCodeInfo(imgData, 1)[0];
		}else	if(null == facePos.getCode()){
			facePos = getFaceApi().getCodeInfo(imgData,facePos);
			if(null == facePos){
				throw new NotFaceDetectedException();
			}
		}
		return this.searchFeatures(facePos.getCode(), similarty, rows, where);
	}
	@Override
	public SearchResult[] searchFaces(MatType matType,byte[] matData, int width, int height, CodeInfo facePos, double similarty, int rows, String where)
			throws NotFaceDetectedException {
		if(facePos == null){
			facePos = getFaceApi().matDetectAndGetCodeInfo(matType, matData, width, height, 1)[0];
		}else	if(null == facePos.getCode()){
			facePos = getFaceApi().matGetCodeInfo(matType,matData, width,height,facePos);
			if(null == facePos){
				throw new NotFaceDetectedException();
			}
		}
		return this.searchFeatures(facePos.getCode(), similarty, rows, where);
	}
	@Override
	public SearchResult[] searchFeatures(byte[] feature, double similarty, int rows, String where) {
		if(null != fdbse){
			return SearchResult.toSearchResult(fdbse.searchFeatures(feature, similarty, rows, (String[])null, 0));
		}else{
			return super.searchFeatures(feature, similarty, rows, where);
		}
	}
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("FacedbFull [faceApi=");
		builder.append(faceApi);
		builder.append(", fse=");
		builder.append(fdbse);
		builder.append("]");
		return builder.toString();
	}
}
